
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: IndexModel.java 317 2010-02-11 22:06:51Z archie.cobbs $
 */

package org.dellroad.sidekar.schema.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.dellroad.sidekar.Sidekar;
import org.dellroad.sidekar.schema.SchemaMismatchException;

/**
 * Schema type representing an index on one or more fields of an entity.
 *
 * @see org.dellroad.sidekar.Index
 */
public class IndexModel extends AbstractModel {

    private final ArrayList<String> fieldNames = new ArrayList<String>();
    private boolean unique;

    // Normal constructor
    public IndexModel() {
    }

    // Copy constructor
    IndexModel(IndexModel that) {
        super(that);
        this.fieldNames.addAll(that.fieldNames);
        this.unique = that.unique;
    }

    public List<String> getFieldNames() {
        return this.fieldNames;
    }

    public boolean isUnique() {
        return this.unique;
    }
    public void setUnique(boolean unique) {
        this.unique = unique;
    }

    // Used by JiBX
    @SuppressWarnings("unused")
    private void addFieldName(String fieldName) {
        this.fieldNames.add(fieldName);
    }

    // Used by JiBX
    @SuppressWarnings("unused")
    private Iterator<String> iterateFieldNames() {
        return this.fieldNames.iterator();
    }

    public void validate(EntityModel entityModel) {

        // At least one field is required
        if (this.fieldNames.isEmpty())
            throw new IllegalArgumentException(this + " has zero indexed fields");

        // Verify no field is listed twice
        HashSet<String> fieldNames = new HashSet<String>();
        for (String name : fieldNames) {
            if (!fieldNames.add(name))
                throw new IllegalArgumentException("duplicate indexed field \"" + name + "\" in " + this);
        }

        // Inventory all available fields
        HashMap<String, FieldModel> fieldMap = new HashMap<String, FieldModel>();
        while (entityModel != null) {
            fieldMap.putAll(entityModel.getFieldModels());
            entityModel = entityModel.getParentModel();
        }

        // Validate the fields being indexed
        for (String fieldName : getFieldNames()) {
            if (Sidekar.ID_FIELD_NAME.equals(fieldName)
              || Sidekar.ENTITY_FIELD_NAME.equals(fieldName)
              || Sidekar.ATTACHED_FIELD_NAME.equals(fieldName))
                continue;
            FieldModel fieldModel = fieldMap.get(fieldName);
            if (fieldModel == null)
                throw new IllegalArgumentException("unknown indexed field \"" + fieldName + "\" in " + this);
            if (!(fieldModel instanceof SimpleFieldModel))
                throw new IllegalArgumentException("indexed field \"" + fieldName + "\" in " + this  + " is not simple");
        }
    }

    public void checkSameIndex(IndexModel that) {
        if (this.isUnique() != that.isUnique()) {
            throw new SchemaMismatchException("different uniqueness: "
              + (that.isUnique() ? "unique" : "non-unique")
              + " vs. "
              + (this.isUnique() ? "unique" : "non-unique"));
        }
        List<String> thisFields = this.fieldNames;
        List<String> thatFields = that.fieldNames;
        if (!thisFields.equals(thatFields))
            throw new SchemaMismatchException("different indexed fields: " + thatFields + " vs. " + thisFields);
    }

    @Override
    public String toString() {
        final StringBuilder buf = new StringBuilder();
        buf.append(isUnique() ? "unique" : "non-unique")
          .append(" index `")
          .append(this.getName())
          .append("' on fields");
        boolean first = true;
        for (String fieldName : getFieldNames()) {
            if (!first)
                buf.append(',');
            else
                first = false;
            buf.append(" \"").append(fieldName).append('"');
        }
        return buf.toString();
    }
}

